﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Reflection;
using System.IO;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
    /// </summary>
    public class MemberBox
    {
        private MethodBase memberObject;
        public Type[] argTypes;
        public Object delegateTo;
        public bool vararg;

        public MemberBox(MethodInfo method)
        {
            init(method);
        }

        public MemberBox(ConstructorInfo constructor)
        {
            init(constructor);
        }

        private void init(MethodInfo method)
        {
            this.memberObject = method;
            ParameterInfo[] param = method.GetParameters();

            argTypes = new Type[param.Length];
            for (int i = 0; i < param.Length; i++)
            {
                argTypes[i] = param[i].ParameterType;
            }
            this.vararg = VMBridge.instance.isVarArgs(method);
        }

        private void init(ConstructorInfo constructor)
        {
            this.memberObject = constructor;
            ParameterInfo[] param = constructor.GetParameters();
            argTypes = new Type[param.Length];
            for (int i = 0; i < param.Length; i++)
            {
                argTypes[i] = param[i].ParameterType;
            }

            this.vararg = VMBridge.instance.isVarArgs(constructor);
        }

        public MethodInfo method()
        {
            return (MethodInfo)memberObject;
        }

        public ConstructorInfo ctor()
        {
            return (ConstructorInfo)memberObject;
        }
        /// <summary>
        /// 是成员
        /// </summary>
        /// <returns></returns>
        public MethodBase member()
        {
            return (MethodBase)memberObject;
        }
        /// <summary>
        /// 是方法
        /// </summary>
        /// <returns></returns>
        public bool isMethod()
        {
            return memberObject is MethodInfo;
        }

        /// <summary>
        /// 是构造函数
        /// </summary>
        /// <returns></returns>
        public bool isCtor()
        {
            return memberObject is ConstructorInfo;
        }
        /// <summary>
        /// 是静态的
        /// </summary>
        /// <returns></returns>
        public bool isStatic()
        {
            //switch( memberObject.MemberType)
            //{
            //    case MemberTypes.Constructor:
            //    case MemberTypes.Method:
            //        MethodBase cons = memberObject as MethodBase;
            //        return cons.IsStatic;
            //        case MemberTypes.Field:
            //       FieldInfo fi =  memberObject as  FieldInfo;
            //        return fi.IsStatic;
            //    //case MemberTypes.Property:
            //    //    PropertyInfo pi = memberObject as PropertyInfo;
            //    //    return pi.iss
            //}
            //throw new Exception("[是否是静态异常]");
            return memberObject.IsStatic;
            //return Modifier.isStatic(memberObject.getModifiers());
        }

        public String getName()
        {
            return memberObject.Name;
        }

        public Type getDeclaringClass()
        {
            return memberObject.DeclaringType;
        }

        public String toJavaDeclaration()
        {
            StringBuilder sb = new StringBuilder();
            if (isMethod())
            {
                MethodInfo method1 = method() as MethodInfo;
                sb.Append(method1.ReturnType);
                sb.Append(' ');
                sb.Append(method1.Name);
            }
            else
            {
                ConstructorInfo ctor1 = ctor();
                String name = ctor1.DeclaringType.Name;
                int lastDot = name.IndexOf('.');
                if (lastDot >= 0)
                {
                    name = name.Substring(lastDot + 1);
                }
                sb.Append(name);
            }
            sb.Append(JavaMembers.liveConnectSignature(argTypes));
            return sb.ToString();
        }


        public String toString()
        {
            return memberObject.ToString();
        }

        public Object invoke(Object target, Object[] args)
        {
            MethodInfo method1 = method();
            try
            {
                try
                {
                    return method1.Invoke(target, args);
                }
                catch (AccessViolationException ex)
                {
                    MethodInfo accessible = searchAccessibleMethod(method1, argTypes);
                    if (accessible != null)
                    {
                        memberObject = accessible;
                        method1 = accessible;
                    }
                    else
                    {
                        if (!VMBridge.instance.tryToMakeAccessible(method1))
                        {
                            throw Context.throwAsScriptRuntimeEx(ex);
                        }
                    }
                    // Retry after recovery
                    return method1.Invoke(target, args);
                }
            }
            catch (Exception ite)
            {
                // Must allow ContinuationPending exceptions to propagate unhindered
                //Throwable e = ite;
                //do {
                //    e = ((InvocationTargetException) e).getTargetException();
                //} while ((e instanceof InvocationTargetException));
                //if (e instanceof ContinuationPending) 
                //    throw (ContinuationPending) e;
                //throw Context.throwAsScriptRuntimeEx(e);      
                //throw Context.throwAsScriptRuntimeEx(ex);
                throw ite;
            }
            //} catch (Exception ex1) {
            //    throw Context.throwAsScriptRuntimeEx(ex);
            //}
        }

        public Object newInstance(Object[] args)
        {
            ConstructorInfo ctor1 = ctor();
            try
            {
                try
                {
                    return ctor1.Invoke(args);
                }
                catch (Exception ex)
                {
                    if (!VMBridge.instance.tryToMakeAccessible(ctor1))
                    {
                        throw Context.throwAsScriptRuntimeEx(ex);
                    }
                }
                return ctor1.Invoke(args);
            }
            catch (Exception ex)
            {
                throw Context.throwAsScriptRuntimeEx(ex);
            }
        }

        private static MethodInfo searchAccessibleMethod(MethodInfo method, Type[] param)
        {
            //int modifiers = method.getModifiers();
            if (method.IsPublic && !method.IsStatic)
            {
                Type c = method.DeclaringType;
                if (!c.IsPublic)
                {
                    String name = method.Name;
                    Type[] intfs = c.GetInterfaces();
                    for (int i = 0, N = intfs.Length; i != N; ++i)
                    {
                        Type intf = intfs[i];
                        if (intf.IsPublic)
                        {
                            try
                            {
                                return intf.GetMethod(name, param);
                            }
                            catch (NotSupportedException ex)
                            {
                            }
                            catch (Exception ex) { }
                        }
                    }
                    for (; ; )
                    {
                        c = c.BaseType;
                        if (c == null) { break; }
                        if (c.IsPublic)
                        {
                            try
                            {
                                MethodInfo m = c.GetMethod(name, param);

                                if (m.IsPublic
                                    && !m.IsStatic)
                                {
                                    return m;
                                }
                            }
                            catch (NotSupportedException ex)
                            {
                            }
                            catch (Exception ex) { }
                        }
                    }
                }
            }
            return null;
        }

        private void readObject(StreamReader in1)
        {
            //in1.defaultReadObject();
            //MethodInfo member = readMember(in);
            //if (member is MethodInfo) {
            //    init((MethodInfo)member);
            //} else {
            //    init((ConstructorInfo)member);
            //}
        }

        private void writeObject(StreamWriter out1)
        {
            //  out.defaultWriteObject();
            //   writeMember(out, memberObject);
        }

        /**
         * Writes a Constructor or Method object.
         *
         * Methods and Constructors are not serializable, so we must serialize
         * information about the class, the name, and the parameters and
         * recreate upon deserialization.
         */
        private static void writeMember(StreamWriter out1, MethodInfo member)
        {
            // if (member == null) {
            //     out.writeBoolean(false);
            //     return;
            // }
            //// out.writeBoolean(true);
            // if (!(member instanceof Method || member instanceof Constructor))
            //     throw new Exception("not Method or Constructor");
            // out.writeBoolean(member instanceof Method);
            // out.writeObject(member.getName());
            // out.writeObject(member.getDeclaringClass());
            // if (member instanceof Method) {
            //     writeParameters(out, ((Method) member).getParameterTypes());
            // } else {
            //     writeParameters(out, ((Constructor<?>) member).getParameterTypes());
            // }
        }

        /**
         * Reads a Method or a Constructor from the stream.
         */
        private static MethodInfo readMember(StreamReader in1)
        {
            //if (!in.readBoolean())
            //    return null;
            //boolean isMethod = in.readBoolean();
            //String name = (String) in.readObject();
            //Class<?> declaring = (Class<?>) in.readObject();
            //Class<?>[] parms = readParameters(in);
            //try {
            //    if (isMethod) {
            //        return declaring.getMethod(name, parms);
            //    } else {
            //        return declaring.getConstructor(parms);
            //    }
            //} catch (Exception e) {
            //    throw new IOException("Cannot find member: " + e);
            //}
            return null;
        }

        private static Type[] primitives = {
        typeof(bool),
         typeof(Byte),
        typeof(void),
         typeof(Double),
         typeof(float),
         typeof(Int32),
         typeof(Int64),
         typeof(Int16)
        
    };

        /**
         * Writes an array of parameter types to the stream.
         *
         * Requires special handling because primitive types cannot be
         * found upon deserialization by the default Java implementation.
         */
        private static void writeParameters(StreamWriter out1, Type[] parms)
        {
            //void
            //    out.writeShort(parms.length);
            //outer:
            //    for (int i=0; i < parms.length; i++) {
            //        Class<?> parm = parms[i];
            //        boolean primitive = parm.isPrimitive();
            //        out.writeBoolean(primitive);
            //        if (!primitive) {
            //            out.writeObject(parm);
            //            continue;
            //        }
            //        for (int j=0; j < primitives.length; j++) {
            //            if (parm.equals(primitives[j])) {
            //                out.writeByte(j);
            //                continue outer;
            //            }
            //        }
            //        throw new IllegalArgumentException("Primitive " + parm +
            //                                           " not found");
            //    }
        }

        /**
         * Reads an array of parameter types from the stream.
         */
        private static Type[] readParameters(StreamReader in1)
        {
            //Type[] result = new Class[in.readShort()];
            //for (int i=0; i < result.length; i++) {
            //    if (!in.readBoolean()) {
            //        result[i] = (Type) in.readObject();
            //        continue;
            //    }
            //    result[i] = primitives[in.readByte()];
            //}
            ////return result;
            return null;
        }
    }// end for class
}
